"""
安全工具

提供密码加密、验证、随机数生成等安全相关功能
"""

import hashlib
import secrets
import string
import re
from typing import Optional


class SecurityUtils:
    """安全工具类"""
    
    @staticmethod
    def hash_password(password: str, salt: Optional[str] = None) -> str:
        """
        对密码进行哈希加密
        
        Args:
            password: 明文密码
            salt: 盐值，如果不提供会自动生成
            
        Returns:
            str: 加密后的密码（格式：salt$hash）
        """
        if salt is None:
            salt = SecurityUtils.generate_salt()
        
        # 使用 PBKDF2 算法进行密码哈希
        password_bytes = password.encode('utf-8')
        salt_bytes = salt.encode('utf-8')
        
        # 使用SHA256，迭代100000次
        hash_bytes = hashlib.pbkdf2_hmac('sha256', password_bytes, salt_bytes, 100000)
        hash_hex = hash_bytes.hex()
        
        return f"{salt}${hash_hex}"
    
    @staticmethod
    def verify_password(password: str, hashed_password: str) -> bool:
        """
        验证密码
        
        Args:
            password: 明文密码
            hashed_password: 哈希密码
            
        Returns:
            bool: 密码是否正确
        """
        try:
            if '$' not in hashed_password:
                return False
            
            salt, stored_hash = hashed_password.split('$', 1)
            computed_hash = SecurityUtils.hash_password(password, salt)
            
            return computed_hash == hashed_password
        except Exception:
            return False
    
    @staticmethod
    def generate_salt(length: int = 16) -> str:
        """
        生成随机盐值
        
        Args:
            length: 盐值长度
            
        Returns:
            str: 随机盐值
        """
        alphabet = string.ascii_letters + string.digits
        return ''.join(secrets.choice(alphabet) for _ in range(length))
    
    @staticmethod
    def generate_session_token(length: int = 32) -> str:
        """
        生成会话令牌
        
        Args:
            length: 令牌长度
            
        Returns:
            str: 随机会话令牌
        """
        return secrets.token_hex(length)
    
    @staticmethod
    def is_valid_username(username: str) -> bool:
        """
        验证用户名格式
        
        Args:
            username: 用户名
            
        Returns:
            bool: 用户名是否合法
        """
        if not username or len(username) == 0:
            return False
        
        if len(username) > 64:
            return False
        
        # 用户名只能包含字母、数字、下划线
        pattern = re.compile(r'^[a-zA-Z0-9_]+$')
        return bool(pattern.match(username))
    
    @staticmethod
    def is_valid_host(host: str) -> bool:
        """
        验证主机格式
        
        Args:
            host: 主机名或IP
            
        Returns:
            bool: 主机格式是否合法
        """
        if not host or len(host) == 0:
            return False
        
        if len(host) > 64:
            return False
        
        # 支持 %（通配符）、localhost、IP地址、域名
        if host == '%' or host == 'localhost':
            return True
        
        # 简单的IP地址格式检查
        ip_pattern = re.compile(r'^(\d{1,3}\.){3}\d{1,3}$')
        if ip_pattern.match(host):
            parts = host.split('.')
            return all(0 <= int(part) <= 255 for part in parts)
        
        # 简单的域名格式检查
        domain_pattern = re.compile(r'^[a-zA-Z0-9.-]+$')
        return bool(domain_pattern.match(host))
    
    @staticmethod
    def is_strong_password(password: str):
        """
        检查密码强度
        
        Args:
            password: 密码
            
        Returns:
            tuple: (是否足够强, 错误信息)
        """
        if len(password) < 8:
            return False, "密码长度至少8位"
        
        if len(password) > 128:
            return False, "密码长度不能超过128位"
        
        # 检查是否包含字母
        if not re.search(r'[a-zA-Z]', password):
            return False, "密码必须包含字母"
        
        # 检查是否包含数字
        if not re.search(r'\d', password):
            return False, "密码必须包含数字"
        
        return True, ""
    
    @staticmethod
    def sanitize_input(input_str: str, max_length: int = 255) -> str:
        """
        清理输入字符串，防止注入攻击
        
        Args:
            input_str: 输入字符串
            max_length: 最大长度
            
        Returns:
            str: 清理后的字符串
        """
        if not input_str:
            return ""
        
        # 去除首尾空白
        cleaned = input_str.strip()
        
        # 限制长度
        if len(cleaned) > max_length:
            cleaned = cleaned[:max_length]
        
        # 去除潜在危险字符（简单实现）
        dangerous_chars = ['<', '>', '"', "'", '&', '\x00', '\n', '\r']
        for char in dangerous_chars:
            cleaned = cleaned.replace(char, '')
        
        return cleaned
    
    @staticmethod
    def mask_sensitive_info(info: str, mask_char: str = '*', visible_chars: int = 3) -> str:
        """
        遮蔽敏感信息
        
        Args:
            info: 敏感信息
            mask_char: 遮蔽字符
            visible_chars: 可见字符数量
            
        Returns:
            str: 遮蔽后的信息
        """
        if not info or len(info) <= visible_chars:
            return mask_char * len(info) if info else ""
        
        visible_part = info[:visible_chars]
        masked_part = mask_char * (len(info) - visible_chars)
        
        return visible_part + masked_part
